/*
	ACDM_window.c

	contains all window (HWND) related functionality for the ACDM

	Author: Aidan Goddard 5/11/13
*/

#include"ACDM_buffer.h"
#include"ACDM_window.h"
#include"ACDM_keyboard.h"
#include"../../kernel/headers/scheduler_functions.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// TABLES

static volatile HWND		active_hwnds[512];
static volatile uint64_t	hwnd_PIDs[512];
static volatile uint8_t		hwnd_states[512];	// bitmap: bit 0 = in use, bit 1 = is_static
												// (1 = static, 0 = console), bit 2 = is_visible

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// COMMON FUNCTIONS

// returns zero if index is invalid
static HWND GetHWNDFromIndex(int index)
{
	return active_hwnds[index];
}

// returns 0 if hwnd is invalid
static int GetIndexFromHWND(HWND hwnd)
{
	// first 9 bits (mask 0x1ff) of hwnd are index
	int index = hwnd & 0x1ff;

	int to_return = 0;
	if(active_hwnds[index] == hwnd)
	{
		to_return = index;
	}

	return to_return;
}

static HWND CreateHWND(uint32_t index)
{
	// get timestamp from CPU
	HWND to_return = GetCPUTimeStamp();

	// shift left 9 bits and add the index
	index = index & 0x1ff;
	to_return = to_return << 9;
	to_return = to_return + index;

	// finished
	return to_return;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// ACTIVE FUNCTIONALITY CALLED BY ACDM PROCESS
// as there is only one instance of the ACDM process, locking is not required over global controls
// in this section

static volatile short buffer_stack[512];
static volatile short buffer_stack_index = 0;

static inline void SetupBufferAllocation()
{
	// build the buffer allocation stack
	// buffers 0 - 15 are reserved, so are not on the stack
	int i;
	for(i = 16; i < 512; i++)
	{
		buffer_stack[i] = (short)i;
	}

	// set stack index
	buffer_stack_index = 16;

	// finished
	return;
}

static inline int AllocateBuffer()
{
	// setup the stack if first time
	if(buffer_stack_index == 0)
	{
		SetupBufferAllocation();
	}

	// value to return
	int to_return = 0;

	// stack index points to next value to allocate
	// check the buffer is not empty
	if(buffer_stack_index < 512)
	{
		// stack is non-empty
		to_return = buffer_stack[buffer_stack_index++];
	}

	// finished
	return to_return;
}

static inline void DeallocateBuffer(int buffer)
{
	// stack index points to next value to allocate
	// shift it back one and write the buffer number to put on it
	buffer_stack[--buffer_stack_index] = (short)buffer;

	// finished
	return;
}

HWND WindowCreateStatic(uint64_t PID, char *window_name, int name_count)
{
	// first get a buffer index and check it is valid
	uint32_t buffer = AllocateBuffer();
	if(buffer == 0)
	{
		return 0;
	}

	// now convert it to a HWND
	HWND to_return = CreateHWND(buffer);

	// set global data
	active_hwnds[buffer] = to_return;
	hwnd_PIDs[buffer] = PID;
	hwnd_states[buffer] = 0x03; // active + is_static

	// create the static buffer
	StaticBufferCreate(buffer, to_return, PID, window_name, name_count);

	// finished
	return to_return;
}

void WindowDestroy(HWND hwnd)
{
	// get buffer index from hwnd
	int buffer = GetIndexFromHWND(hwnd);
	if(buffer == 0)
	{
		return;
	}

	// first set the window to be unused
	active_hwnds[buffer] = 0;
	hwnd_PIDs[buffer] = 0;
	hwnd_states[buffer] = 0;

	// deallocate the buffer index
	DeallocateBuffer(buffer);

	// finished
	return;
}

void WindowGetForProcess(uint64_t PID, HWND *windows, uint32_t *total_windows)
{
	// scan each HWND for a matching PID and deallocate any matches
	int i, k;
	for(i = 16, k = 0; i < 512; i++)
	{
		if(hwnd_PIDs[i] == PID)
		{
			windows[k++] = active_hwnds[i];
			*total_windows += 1;
		}
	}

	// finished
	return;
}

void WindowHide(HWND hwnd)
{
	// get buffer index from hwnd
	int buffer = GetIndexFromHWND(hwnd);
	if(buffer == 0)
	{
		return;
	}

	// set the window to non-visible
	uint8_t a = hwnd_states[buffer];
	hwnd_states[buffer] = a & 0xfb;	// clear bit 2

	// finished
	return;
}

void WindowShow(HWND hwnd)
{
	// get buffer index from hwnd
	int buffer = GetIndexFromHWND(hwnd);
	if(buffer == 0)
	{
		return;
	}

	// set the window to visible
	uint8_t a = hwnd_states[buffer];
	hwnd_states[buffer] = a | 0x04; // set bit 2

	// finished
	return;
}

void WindowGiveFocus(HWND hwnd)
{
	// get buffer index from hwnd
	int buffer = GetIndexFromHWND(hwnd);
	if(buffer == 0)
	{
		return;
	}

	// check if window is invisible
	if((hwnd_states[buffer] & 4) == 0)
	{
		return;
	}

	// switch and update the buffer
	SwitchBuffer(buffer);
	UpdateVideoBuffer(buffer);

	// change the window name accordingly (check this?)
	char *window_name = GetWindowName(buffer);
	UpdateWindowName(window_name);
	UpdateRibbons();

	// finished
	return;
}

void WindowChangeName(HWND hwnd, char *window_name, int name_count)
{
	// get buffer index from hwnd
	int buffer = GetIndexFromHWND(hwnd);
	if(buffer == 0)
	{
		return;
	}

	// change the name
	ChangeWindowName(buffer, window_name, name_count);

	// finished
	return;
}

void WindowGetAllActive(HWND *active_list, uint32_t *active_count)
{
	// go through each HWND and copy across the active ones
	int i, k;
	for(i = 16, k = 0; i < 512; i++)
	{
		// get the state value
		uint8_t a = hwnd_states[i];

		// check the window is active and visible
		if((a & 1) && (a & 4))
		{
			active_list[k++] = active_hwnds[i];
		}
	}

	// finished
	*active_count = k;
	return;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// PASSIVE FUNCTIONALITY

// function to get a pointer to a static buffer window
uint64_t WindowGetStaticBufferAddress(HWND hwnd)
{
	// check hwnd is valid
	int index = GetIndexFromHWND(hwnd);
	if(index == 0)
	{
		return 0;
	}

	// get the buffer address
	return GetStaticBufferAddress(index);
}

// function to update a window
void WindowUpdate(HWND hwnd)
{
	// check hwnd is valid
	int index = GetIndexFromHWND(hwnd);
	if(index == 0)
	{
		return;
	}

	// update the video buffer
	UpdateVideoBuffer(index);

	// finished
	return;
}


// keyboard functions
uint8_t WindowGetASCIIChar(HWND hwnd)
{
	// check if hwnd is valid
	int index = GetIndexFromHWND(hwnd);
	if(index == 0)
	{
		return 0;
	}

	// get the key
	uint8_t to_return = KeyboardGetASCIIChar(index);
	return to_return;
}

void WindowClearASCIIChar(HWND hwnd)
{
	// check if hwnd is valid
	int index = GetIndexFromHWND(hwnd);
	if(index == 0)
	{
		return;
	}

	// get the key
	KeyboardClearASCIIChar(index);
	return;
}

void WindowGetKeyState(HWND hwnd, uint8_t *destination_key_state)
{
	return;
}
